/**
 * @author wItspirit
 * 28-mei-2005
 * FlickrApi.java
 */

package org.scohen.juploadr.uploadapi;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Decorations;
import org.scohen.juploadr.Messages;
import org.scohen.juploadr.app.ImageAttributes;
import org.scohen.juploadr.app.JUploadr;
import org.scohen.juploadr.app.PhotoSet;
import org.scohen.juploadr.app.UploadImage;
import org.scohen.juploadr.event.UploadCompleteEvent;
import org.scohen.juploadr.event.UploadStatusMonitor;
import org.scohen.juploadr.prefs.Configuration;
import org.scohen.juploadr.ui.MessageUtils;
import org.scohen.juploadr.ui.PopupHandler;
import org.scohen.juploadr.ui.ReusableUIFactory;
import org.scohen.juploadr.upload.HttpClientFactory;
import org.scohen.juploadr.uploadapi.abilities.Abilities;
import org.scohen.juploadr.uploadapi.abilities.DefaultAbilities;
import org.scohen.juploadr.util.JUploadrBrowserLauncher;

/**
 * This is the overal FlickrApi
 * 
 * It gives some direct access to the methods we utilize from the Flickr API In
 * the future a further split-up may be required to keep the number of methods
 * manageable
 * 
 */
public abstract class ImageUploadApi {
    private static ImageUploadApi[] implementations = null;
    private static ImageUploadApi instance = null;
    protected Image icon;
    protected DefaultAbilities abilities = new DefaultAbilities();

    static {
        if (implementations == null) {
            try {
                implementations = new PluginLocator().getApis();
            } catch (NoPluginsFoundException e) {
                MessageUtils
                        .showErrorMessage("Could not load any photo upload plugins");
            }
        }

    }

    public static synchronized ImageUploadApi getInstance() {

        if (instance == null) {
            try {
                for (int i = 0; i < implementations.length; i++) {
                    if (Configuration.getInstance().getUploadApiClassName().equalsIgnoreCase(
                            implementations[i].getClass().getName())) {
                        instance = implementations[i];
                        break;
                    }
                }

            } catch (Exception e) {

            }
            try {
                if (!instance.isCurrentAccountNullAccount()) {
                    instance.authenticate(instance.getCurrentAccount());
                }

            } catch (Exception e) {
            }
            versionCheck();
            instance.initialize();
        }

        return instance;
    }

    public static synchronized final void changeImplementation(String newImplementation, JUploadr parent) {
        Configuration.getInstance().setUploadApiClassName(newImplementation);
        instance = null;
        getInstance().initialize();
        Iterator photos = parent.getImageAttributes().values().iterator();
        // mark all uploaded images as new.
        while (photos.hasNext()) {
            ImageAttributes attr = (ImageAttributes) photos.next();
            attr.setState(UploadImage.STATE_NEW);
        }
        parent.refresh();

    }

    public synchronized Image getIcon() {
        if (icon == null) {
            String res = this.getClass().getPackage().getName() + ".resources.";
            res = res.replace('.', '/');
            icon = new Image(ReusableUIFactory.getInstance().getDisplay(), getClass().getClassLoader()
                    .getResourceAsStream(res + "icon.png"));
        }
        return icon;

    }

    /**
     * Returns the friendly name of the upload site. Like "Flickr" or "Zooomr"
     * 
     * @return the name of the site.
     */
    public abstract String getSiteName();

    /**
     * Returns the currently enabled account
     * 
     * @return
     */
    public abstract Account getCurrentAccount();

    /**
     * Creates a new account with the given name
     * 
     * @param name
     *            the name to give the account
     * @return a new account with the name given
     */
    public abstract Account newAccount(String name);

    /**
     * Sets the currently enabled acconut to the one passed in
     * 
     * @param account
     *            the account to enable.
     */
    public abstract void setCurrentAccount(Account account);

    /**
     * Do any initialization stuff that needs to be done. this method does not
     * throw exceptions, as it's called when jUploadr is started, and we don't
     * want to have it crash on startup. This is not guaranteed to be the first
     * method called on your newly created object. In fact, authenticate() will
     * probably be called first.
     * 
     */
    public abstract void initialize();

    /**
     * Authenticate with the new flickr auth api.
     * 
     * @return true if OK was clicked, false if cancel was clicked
     * @throws ProtocolException
     * @throws CommunicationException
     */
    public abstract boolean authenticate(Account toAuthenticate) throws ProtocolException, CommunicationException;

    /**
     * Uploads the image and data in <code>imageAttributes</code> for Flickr
     * user <code>userInfo</code> and notifying <code>monitor</code> of
     * upload bar.
     * 
     * @param userInfo
     *            Information about the user that wants to upload
     * @param imageAttributes
     *            the image to be uploaded
     * @param monitor
     *            The monitor that should be notified of upload bar
     * @throws UploadFailedException
     *             When a problem occurs during the uploading
     */
    public abstract void upload(UploadImage imageAttributes, UploadStatusMonitor monitor)
            throws UploadFailedException, ProtocolException, CommunicationException;

    /**
     * Returns the class names of configuration dialogs for this API.
     * 
     * @return
     */
    public abstract Class[] getConfiurationDialogs();

    public abstract PopupHandler windowRightClicked(Decorations parent, JUploadr clicked);

    public abstract PopupHandler thumbnailRightClicked(Decorations parent, JUploadr uploadr,
            ImageAttributes clicked);

    public abstract void postProcessUpload(UploadCompleteEvent e, ImageAttributes atts);

    public abstract PhotoSet newPhotoSet(ImageAttributes primaryPhoto);
    public abstract PhotoSet newPhotoSet(String name, String description, ImageAttributes primaryPhoto);
    
    /**
     * Event handler that is fired after all photos have finished uploading
     * 
     * @param collection
     *            All the photos that have been uploaded: consists of
     *            ImageAttributes objects
     */
    public abstract void postProcessAllUploads(Collection<? extends UploadImage> collection);

    public boolean isCurrentAccountAuthenticated() {
        return getCurrentAccount().isAuthenticated();
    }

    public abstract boolean isCurrentAccountNullAccount();

    public void authenticateCurrentAccount() throws ProtocolException, CommunicationException {
        authenticate(getCurrentAccount());
    }
    
    /**
     * Returns the abilities of the current photo upload application.
     * @return an {@link Abilities} object that defines the operations that the current upload api supports. 
     */
    public Abilities getAbilities() {
        return abilities;
    }

    public static ImageUploadApi[] getRegisteredImplemtations() {
        return implementations;
    }

    /**
     * Get a list of all the accounts in this protocol implementation
     * 
     * @return a list of Account objects
     */
    public abstract List<? extends Account> allAccounts();

    protected static void versionCheck() {
        Configuration config = Configuration.getInstance();
        HttpClient client = HttpClientFactory.getHttpClient();
        GetMethod get = new GetMethod("http://juploadr.sourceforge.net/version.txt"); //$NON-NLS-1$
        try {
            int code = client.executeMethod(get);
            if (code == HttpStatus.SC_OK) {
                String version = get.getResponseBodyAsString();
                if (version != null) {
                    version = version.trim();
                    if (config.shouldUpgradeTo(version)) {
                        if (MessageUtils.showMessage(Messages
                                .getString("juploadr.ui.message.new.version.available") //$NON-NLS-1$
                                + Messages.getString("juploadr.ui.message.open.home.page"), //$NON-NLS-1$
                                Messages.getString("juploadr.ui.message.new.version"), SWT.ICON_QUESTION //$NON-NLS-1$
                                        | SWT.YES | SWT.NO)) {
                            JUploadrBrowserLauncher.openURL("http://juploadr.sourceforge.net");
                        }
                        config.setLastVersionAcked(version);
                    }
                }
            }
        } catch (HttpException e) {
        } catch (IOException e) {

        }
    }

    public static List<? extends Account> allAccountsForAllApis() {
        List<Account> accounts = new LinkedList<Account>();
        for (int i = 0; i < implementations.length; i++) {
            accounts.addAll(implementations[i].allAccounts());
        }
        Collections.sort(accounts, new Comparator<Account>() {
            public int compare(Account a1, Account a2) {
                if (a1.getClass().getName().equals(a2.getClass().getName())) {
                    return a1.getName().compareToIgnoreCase(a2.getName());
                } else {
                    return a1.getClass().getName().compareToIgnoreCase(a2.getClass().getName());
                }
            }
        });
        return accounts;
    }
}
